package com.googlecode.stil.playground;

import android.hardware.SensorManager;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.joints.MouseJoint;
import com.badlogic.gdx.physics.box2d.joints.MouseJointDef;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import org.andengine.engine.Engine;
import org.andengine.engine.FixedStepEngine;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.WakeLockOptions;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.entity.IEntity;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.extension.physics.box2d.util.Vector2Pool;
import org.andengine.extension.physics.box2d.util.constants.PhysicsConstants;
import org.andengine.input.sensor.acceleration.AccelerationData;
import org.andengine.input.sensor.acceleration.IAccelerationListener;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.ui.activity.BaseGameActivity;

import java.io.IOException;

public class PhysicsApplication_rope extends BaseGameActivity implements IAccelerationListener, IOnSceneTouchListener {

	public static int cameraWidth = 800;
	public static int cameraHeight = 480;
	public Scene mScene;
	public FixedStepPhysicsWorld mPhysicsWorld;
	public Body groundWallBody;
	public Body roofWallBody;
	public Body leftWallBody;
	public Body rightWallBody;


	Vector2 localMouseJointTarget = new Vector2();
	MouseJointDef mouseJointDef;
	MouseJoint mouseJoint;
	Body mouseJointGround;
	Body RopeHingeBody;


	@Override
	public void onAccelerationAccuracyChanged(AccelerationData pAccelerationData) {

	}

	@Override
	public void onAccelerationChanged(AccelerationData pAccelerationData) {
		final Vector2 gravity = Vector2Pool.obtain(pAccelerationData.getX(), pAccelerationData.getY());
		this.mPhysicsWorld.setGravity(gravity.mul(2));
		Vector2Pool.recycle(gravity);
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
		if (pSceneTouchEvent.isActionDown()) {
			mouseJointDef.target.set(RopeHingeBody.getWorldCenter());
			mouseJoint = (MouseJoint) mPhysicsWorld.createJoint(mouseJointDef);
			final Vector2 vec = Vector2Pool.obtain(pSceneTouchEvent.getX() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, pSceneTouchEvent.getY() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
			mouseJoint.setTarget(vec);
			Vector2Pool.recycle(vec);
		} else if (pSceneTouchEvent.isActionMove()) {
			final Vector2 vec = Vector2Pool.obtain(pSceneTouchEvent.getX() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, pSceneTouchEvent.getY() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
			mouseJoint.setTarget(vec);
			Vector2Pool.recycle(vec);
			return true;
		} else if (pSceneTouchEvent.isActionCancel() || pSceneTouchEvent.isActionOutside() || pSceneTouchEvent.isActionUp()) {
			mPhysicsWorld.destroyJoint(mouseJoint);
		}
		return true;
	}

	@Override
	public EngineOptions onCreateEngineOptions() {
		EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new FillResolutionPolicy(), new Camera(0, 0, cameraWidth, cameraHeight));
		engineOptions.getRenderOptions().setDithering(true);
		engineOptions.getRenderOptions().getConfigChooserOptions().setRequestedMultiSampling(true);
		engineOptions.setWakeLockOptions(WakeLockOptions.SCREEN_ON);
		return engineOptions;
	}

	@Override
	public Engine onCreateEngine(final EngineOptions pEngineOptions) {
		return new FixedStepEngine(pEngineOptions, 60);
	}

	@Override
	public void onCreateResources(OnCreateResourcesCallback pOnCreateResourcesCallback) throws IOException {

		pOnCreateResourcesCallback.onCreateResourcesFinished();
	}

	@Override
	public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback) throws IOException {
		mScene = new Scene();
		mScene.setBackground(new Background(0.9f, 0.9f, 0.9f));
		pOnCreateSceneCallback.onCreateSceneFinished(mScene);
	}

	@Override
	public void onPopulateScene(Scene pScene, OnPopulateSceneCallback pOnPopulateSceneCallback) throws IOException {
		this.enableAccelerationSensor(this);
		mPhysicsWorld = new FixedStepPhysicsWorld(60, new Vector2(0f, -SensorManager.GRAVITY_EARTH * 2), false, 8, 3);
		mScene.registerUpdateHandler(mPhysicsWorld);
		final FixtureDef WALL_FIXTURE_DEF = PhysicsFactory.createFixtureDef(0, 0.1f, 0.5f);
		final Rectangle ground = new Rectangle(cameraWidth / 2f, 6f, cameraWidth - 4f, 8f, this.getVertexBufferObjectManager());
		final Rectangle roof = new Rectangle(cameraWidth / 2f, cameraHeight - 6f, cameraWidth - 4f, 8f, this.getVertexBufferObjectManager());
		final Rectangle left = new Rectangle(6f, cameraHeight / 2f, 8f, cameraHeight - 4f, this.getVertexBufferObjectManager());
		final Rectangle right = new Rectangle(cameraWidth - 6f, cameraHeight / 2f, 8f, cameraHeight - 4f, this.getVertexBufferObjectManager());
		ground.setColor(0f, 0f, 0f);
		roof.setColor(0f, 0f, 0f);
		left.setColor(0f, 0f, 0f);
		right.setColor(0f, 0f, 0f);
		groundWallBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, ground, BodyDef.BodyType.StaticBody, WALL_FIXTURE_DEF);
		roofWallBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, roof, BodyDef.BodyType.StaticBody, WALL_FIXTURE_DEF);
		leftWallBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, left, BodyDef.BodyType.StaticBody, WALL_FIXTURE_DEF);
		rightWallBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, right, BodyDef.BodyType.StaticBody, WALL_FIXTURE_DEF);
		this.mScene.attachChild(ground);
		this.mScene.attachChild(roof);
		this.mScene.attachChild(left);
		this.mScene.attachChild(right);
// Further recipes in this chapter will require us
// 	to place code here.

		final Rectangle RopeHinge = new Rectangle(400f, 440f, 20f, 20f, this.getVertexBufferObjectManager());
		final FixtureDef RopeHingeFixtureDef = PhysicsFactory.createFixtureDef(1000f, 0.5f, 0.5f);
		RopeHingeBody = PhysicsFactory.createBoxBody(mPhysicsWorld, RopeHinge, BodyDef.BodyType.DynamicBody, RopeHingeFixtureDef);
		pScene.attachChild(RopeHinge);
		RopeHingeBody.setLinearDamping(100f);
		mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(RopeHinge, RopeHingeBody));
		new Rope(RopeHingeBody, 10, 25f, 10f, 2f, 5f, 50f, mScene, mPhysicsWorld, this.getVertexBufferObjectManager());
		mouseJointGround = mPhysicsWorld.createBody(new BodyDef());
		mouseJointDef = new MouseJointDef();
		mouseJointDef.bodyA = mouseJointGround;
		mouseJointDef.bodyB = RopeHingeBody;
		mouseJointDef.dampingRatio = 0.0f;
		mouseJointDef.frequencyHz = 80f;
		mouseJointDef.maxForce = (1000.0f * RopeHingeBody.getMass());
		mouseJointDef.collideConnected = false;

		////////////////////////////////////////////////

		mScene.setOnSceneTouchListener(this);
		pOnPopulateSceneCallback.onPopulateSceneFinished();
	}

	@Override
	public void onResumeGame() {
		super.onResumeGame();
		this.enableAccelerationSensor(this);
	}

	@Override
	public void onPauseGame() {
		super.onPauseGame();
		this.disableAccelerationSensor();
	}

	public class Rope extends Object {

		public final int numRopeSegments;
		public final float ropeSegmentsLength;
		public final float ropeSegmentsWidth;
		public final float ropeSegmentsOverlap;
		public final Rectangle[] RopeSegments;
		public final Body[] RopeSegmentsBodies;
		public FixtureDef RopeSegmentFixtureDef;

		public Rope(Body pAttachTo, final int pNumRopeSegments, final float pRopeSegmentsLength, final float pRopeSegmentsWidth, final float pRopeSegmentsOverlap, final float pMinDensity, final float pMaxDensity, IEntity pScene, PhysicsWorld pPhysicsWorld, VertexBufferObjectManager pVertexBufferObjectManager) {
			numRopeSegments = pNumRopeSegments;
			ropeSegmentsLength = pRopeSegmentsLength;
			ropeSegmentsWidth = pRopeSegmentsWidth;
			ropeSegmentsOverlap = pRopeSegmentsOverlap;
			RopeSegments = new Rectangle[numRopeSegments];
			RopeSegmentsBodies = new Body[numRopeSegments];
			RopeSegmentFixtureDef = PhysicsFactory.createFixtureDef(pMaxDensity, 0.01f, 0.0f);
			for (int i = 0; i < numRopeSegments; i++) {
				final RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
				if (i < 1)
					RopeSegments[i] = new Rectangle(pAttachTo.getWorldCenter().x * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, (pAttachTo.getWorldCenter().y * PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT) - (ropeSegmentsLength / 2) + ropeSegmentsOverlap, ropeSegmentsWidth, ropeSegmentsLength, pVertexBufferObjectManager);
				else
					RopeSegments[i] = new Rectangle(RopeSegments[i - 1].getX(), RopeSegments[i - 1].getY() - RopeSegments[i - 1].getHeight() + ropeSegmentsOverlap, ropeSegmentsWidth, ropeSegmentsLength, pVertexBufferObjectManager);
				RopeSegments[i].setColor(0.97f, 0.75f, 0.54f);
				if (i > 0) RopeSegmentFixtureDef.density -= (pMaxDensity - pMinDensity) / numRopeSegments;
				RopeSegmentsBodies[i] = PhysicsFactory.createCircleBody(pPhysicsWorld, RopeSegments[i], BodyDef.BodyType.DynamicBody, RopeSegmentFixtureDef);
				RopeSegmentsBodies[i].setAngularDamping(4f);
				RopeSegmentsBodies[i].setLinearDamping(0.5f);
				RopeSegmentsBodies[i].setBullet(true);
				if (i < 1)
					revoluteJointDef.initialize(pAttachTo, RopeSegmentsBodies[i], pAttachTo.getWorldCenter());
				else
					revoluteJointDef.initialize(RopeSegmentsBodies[i - 1], RopeSegmentsBodies[i], new Vector2(RopeSegmentsBodies[i - 1].getWorldCenter().x, RopeSegmentsBodies[i - 1].getWorldCenter().y - (ropeSegmentsLength / 2) / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT));
				PhysicsConnector RopeSegmentPhysConnector = new PhysicsConnector(RopeSegments[i], RopeSegmentsBodies[i]);
				pPhysicsWorld.registerPhysicsConnector(RopeSegmentPhysConnector);
				revoluteJointDef.collideConnected = false;
				pPhysicsWorld.createJoint(revoluteJointDef);
				pScene.attachChild(RopeSegments[i]);
			}
		}

	}
}